package cn.cyh.imeans.utils;

import java.awt.geom.Point2D;
import java.util.List;

public class PositioningUtil {
    /**
     * 判断点是否在多边形内
     *
     * @param point 测试点
     * @param pts   多边形的点
     * @return boolean
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts) {

        int N = pts.size();
        boolean boundOrVertex = true;
        //交叉点数量
        int intersectCount = 0;
        //浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        //临近顶点
        Point2D.Double p1, p2;
        //当前点
        Point2D.Double p = point;

        p1 = pts.get(0);
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }

            p2 = pts.get(i % N);
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                if (p.y <= Math.max(p1.y, p2.y)) {
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) { //nosonar
                        return boundOrVertex;
                    }

                    if (p1.y == p2.y) { //nosonar
                        if (p1.y == p.y) { //nosonar
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(p.y - xinters) < precision) {
                            return boundOrVertex;
                        }

                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (p.x == p2.x && p.y <= p2.y) { //nosonar
                    Point2D.Double p3 = pts.get((i + 1) % N);
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        //偶数在多边形外
        if (intersectCount % 2 == 0) {
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 平均半径,单位：m
     */
    private static final double EARTH_RADIUS = 6371393;

    /**
     * 计算两点之间的距离,用来判断是否在圆上
     *
     * @param source
     * @param target
     * @return double
     */
    public static double isInCircle(Point2D.Double source, Point2D.Double target) {
        double radLat1 = rad(source.getX());
        double radLat2 = rad(target.getX());
        double a = radLat1 - radLat2;
        double b = rad(source.getY()) - rad(target.getY());
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000.0;   //nosonar
        return s;
    }

    /**
     * 计算一线两点和GPS定位之间的距离,用来判断是否在线路上
     *
     * @param start
     * @param end
     * @param target
     * @return
     */

    public static double pointToLine(Point2D.Double start, Point2D.Double end, Point2D.Double target) {
        double space = 0;
        double a, b, c;
        // 线段的长度
        a = isInCircle(start, end);
        // (x1,y1)到点的距离
        b = isInCircle(start, target);
        // (x2,y2)到点的距离
        c = isInCircle(end, target);
        if (c <= 0.000001 || b <= 0.000001) {
            space = 0;
            return space;
        }
        if (a <= 0.000001) {
            space = b;
            return space;
        }
        if (c * c >= a * a + b * b) {
            space = b;
            return space;
        }
        if (b * b >= a * a + c * c) {
            space = c;
            return space;
        }
        // 半周长
        double p = (a + b + c) / 2;
        // 海伦公式求面积
        double s = Math.sqrt(p * Math.abs(p - a) * Math.abs(p - b) * Math.abs(p - c));
        // 返回点到线的距离（利用三角形面积公式求高）
        space = 2 * s / a;
        return space;
    }

    /**
     * 求点到多边形中 每条边
     * 最小的高
     *
     * @param point 点
     * @param pts   多边形的点
     * @return double
     */
    public static double getMinHeight(Point2D.Double point, List<Point2D.Double> pts) {
        if (pts == null || pts.size() < 2) {
            return -1d;
        }
        Point2D.Double aDouble = pts.get(0);
        pts.add(aDouble);
        double min = -1d;
        for (int i = 0; i < pts.size() - 1; i++) {
            double line = pointToLine(pts.get(i), pts.get(i + 1), point);
            if (line < min || min < 0) {
                min = line;
            }
        }
        return min;
    }
}
